package polygon.cli.command;

import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import polygon.cli.Command;
import polygon.cli.CommandException;
import polygon.cli.Layout;
import polygon.cli.util.DownloadUtil;
import polygon.cli.util.ProblemUtil;
import polygon.cli.util.StatementsUtil;
import polygon.util.FileUtil;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * @author MikeMirzayanov (mirzayanovmr@gmail.com)
 */
@SuppressWarnings({"AccessStaticViaInstance"})
public class EjudgeDeployContestCommand extends Command {
    private static final Logger logger = Logger.getLogger(EjudgeDeployProblemCommand.class);

    public String getDescription() {
        return "(Ejudge deploy contest): updates contest in the ejudge installation";
    }

    public void setupOptions() {
        Option contestDir = OptionBuilder.withArgName("contestDir")
                .hasArg()
                .withDescription("Contest directory (example: /home/judges/010203), " +
                        "short form means directory in /home/judges (example: 010203)")
                .create("contestDir");
        getOptions().addOption(contestDir);

        Option url = OptionBuilder.withArgName("url")
                .hasArg()
                .withDescription("Contest URL (example: http://polygon.server.ru:8080/c/123)")
                .create("url");
        getOptions().addOption(url);

        Option problemLetters = OptionBuilder.withArgName("problemLetters")
                .hasArg()
                .withDescription("Letters of problems that you want to redeploy (example: ABE)")
                .create("problemLetters");
        getOptions().addOption(problemLetters);
    }

    public void run() {
        Scanner scanner = new Scanner(System.in);

        String url = getCommandLine().getOptionValue("url");
        if (url == null) {
            System.out.println("Enter contest URL (example: http://polygon.server.ru:8080/c/123):");
            url = scanner.nextLine();
        }

        String contestDir = getCommandLine().getOptionValue("contestDir");
        if (contestDir == null) {
            System.out.println("Enter contest directory (press ENTER for current directory):");
            contestDir = scanner.nextLine();
            if (contestDir.equals("")) {
                contestDir = ".";
            }
        }

        Set<String> problemLetters = new HashSet<String>();
        String problemLettersOption = getCommandLine().getOptionValue("problemLetters");
        if (problemLettersOption != null) {
            for (char letter : problemLettersOption.toCharArray()) {
                problemLetters.add(Character.toString(Character.toLowerCase(letter)));
            }
        }

        Map<String, String> params = new HashMap<String, String>();
        params.put("contestDir", new File(contestDir).getName());
        params.put("contestId", getContestIdByUrl(url));

        File tempDir = null;
        try {
            tempDir = FileUtil.createTemporaryDirectory("EjudgeDeployContestCommand");
            process(url, contestDir, tempDir, params, problemLetters);
        } catch (IOException e) {
            throw new CommandException("Can't create temporary directory.", e);
        } finally {
            if (tempDir != null) {
                try {
                    FileUtil.deleteTotaly(tempDir);
                } catch (IOException e) {
                    // No operations.
                }
            }
        }
    }

    private String getContestIdByUrl(String url) {
        String[] tokens = url.split("/");

        if (tokens.length > 0 && !tokens[tokens.length - 1].isEmpty()) {
            return tokens[tokens.length - 1];
        }

        if (tokens.length > 1 && !tokens[tokens.length - 2].isEmpty()) {
            return tokens[tokens.length - 2];
        }

        return "";
    }

    private void process(String url, String contestDir, File tempDir,
                         Map<String, String> params, Set<String> problemLetters) {
        if (!processStatements(url, contestDir, "russian", params)) {
            processStatements(url, contestDir, "english", params);
        }

        File contestDescriptorFile = new File(tempDir, "contest.xml");
        try {
            String contestXml = DownloadUtil.download(new URL(url + "/contest.xml"), getCli().getLogin(), getCli().getPassword());
            FileUtils.writeStringToFile(contestDescriptorFile, contestXml, "UTF-8");
        } catch (Exception e) {
            throw new CommandException("Can't download contest descriptor", e);
        }

        try {
            NodeList problemsList = FileUtil.extractFromXml(contestDescriptorFile, "/contest/problems/problem", NodeList.class);
            for (int i = 0; i < problemsList.getLength(); i++) {
                Element problem = (Element) problemsList.item(i);

                String letter = problem.getAttribute("index");
                String problemUrl = problem.getAttribute("url");

                if (!problemLetters.isEmpty() && !problemLetters.contains(letter.toLowerCase())) {
                    continue;
                }

                File packageDir = null;
                try {
                    packageDir = ProblemUtil.download(new URL(problemUrl), getCli().getLogin(), getCli().getPassword());
                    ProblemUtil.ejudgeDeployOrUpdateProblem(packageDir,
                            logger, new File(contestDir), letter.toUpperCase());
                } finally {
                    if (packageDir != null) {
                        try {
                            FileUtil.deleteTotaly(packageDir);
                        } catch (IOException e) {
                            // No operations.
                        }
                    }
                }
                logger.info("Problem \"" + letter + ": " + problem.getAttribute("name") + "\" has been deployed or updated");
            }
        } catch (Exception e) {
            throw new CommandException("Can't process contest or contest problem.", e);
        }
    }

    @SuppressWarnings({"ResultOfMethodCallIgnored"})
    private boolean processStatements(String contestUrl, String contestDir, String language, Map<String, String> params) {
        String layoutStatements = Layout.getContestStatements(params);
        if (layoutStatements.isEmpty()) {
            return false;
        }

        String statementsUrl = contestUrl + "/" + language + "/statements.pdf";
        if (contestUrl.endsWith("/")) {
            statementsUrl = contestUrl + language + "/statements.pdf";
        }

        try {
            byte[] bytes = StatementsUtil.download(new URL(statementsUrl), getCli().getLogin(), getCli().getPassword());
            if (bytes.length > 128 && StatementsUtil.looksLikePdf(bytes)) {
                File statements = new File(contestDir, layoutStatements + ".pdf");
                statements.getParentFile().mkdirs();
                FileUtil.writeFile(statements, bytes);
                logger.info("Statements [language=\"" + language + "\"] has been copied");
            }
            return true;
        } catch (IOException e) {
            logger.error(e);
            return false;
        }
    }
}
